package com.cloudshop.service.user.service.impl;

import com.cloudshop.service.common.dto.SendPhoneCodeParamsDto;
import com.cloudshop.service.common.enums.JwtKeyEnum;
import com.cloudshop.service.common.exception.LoginException;
import com.cloudshop.service.common.utils.CheckEmptyUtil;
import com.cloudshop.service.common.utils.JwtTokenUtil;
import com.cloudshop.service.user.dao.jpa.UserRepository;
import com.cloudshop.service.user.dao.mybatis.UserMapper;
import com.cloudshop.service.user.dto.user.LoginParamsDto;
import com.cloudshop.service.user.dto.user.LoginResultDto;
import com.cloudshop.service.user.dto.user.LoginServiceDto;
import com.cloudshop.service.user.dto.user.RegistryParamsDto;
import com.cloudshop.service.user.entity.Users;
import com.cloudshop.service.user.service.UserService;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.amqp.core.AmqpAdmin;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;

/**
 * Users Service.
 *
 * @author 宫新程
 * @since 2019/4/1 14:09
 */
@Slf4j
@Service
public class UserServiceImpl implements UserService {

  private static final String REDIS_PHONE_CODE_PREFIX = "phone_code:";

  @Resource private UserRepository userRepository;
  @Resource private UserMapper userMapper;
  @Resource private BCryptPasswordEncoder encoder;
  @Resource private AmqpAdmin amqpAdmin;
  @Resource private AmqpTemplate amqpTemplate;
  @Resource private StringRedisTemplate stringRedisTemplate;

  private static final String EXCHANGE_NAME = "cloud";

  /** 初始化mq. */
  @PostConstruct
  public void init() {
    // 创建交换器
    DirectExchange exchange = new DirectExchange(EXCHANGE_NAME, true, false);
    amqpAdmin.declareExchange(exchange);

    String[] queuesName = new String[] {"phoneCode"};
    for (String queueName : queuesName) {
      Queue queue = new Queue(queueName, true);
      log.info("初始化Queue:{},成功", queueName);
      amqpAdmin.declareQueue(queue);

      Binding binding = BindingBuilder.bind(queue).to(exchange).with(queueName);
      log.info("初始化Binding成功");
      amqpAdmin.declareBinding(binding);
    }
  }

  /** 查询全部用户. */
  @Override
  public List<Users> findAll() {
    // 判断权限
    List<String> permissionCodeList = JwtTokenUtil.getPermissionCodes();
    // 判断是否是管理员权限
    if (!permissionCodeList.contains("manager")) {
      throw new RuntimeException("权限不足");
    }
    return userRepository.findAll();
  }

  /** 登录. */
  @Override
  public LoginResultDto login(LoginParamsDto paramsDto) {
    List<LoginServiceDto> entityList = userMapper.login(paramsDto);
    if (CheckEmptyUtil.isEmpty(entityList)) {
      throw new LoginException("用户名不存在");
    }
    LoginServiceDto entity = entityList.get(0);
    if (!encoder.matches(paramsDto.getPassword(), entity.getPassword())) {
      throw new LoginException("用户名或密码错误");
    }
    // 获取权限编码列表
    List<String> permissionCodeList =
        entityList
            .parallelStream()
            .filter(e -> CheckEmptyUtil.isNotEmpty(e.getPermissionCode()))
            .map(LoginServiceDto::getPermissionCode)
            .distinct()
            .collect(Collectors.toList());
    // 获取权限名称列表
    List<String> permissionNameList =
        entityList
            .parallelStream()
            .filter(e -> CheckEmptyUtil.isNotEmpty(e.getPermissionName()))
            .map(LoginServiceDto::getPermissionName)
            .distinct()
            .collect(Collectors.toList());
    // 获取角色列表
    List<String> roleNameList =
        entityList
            .parallelStream()
            .filter(e -> CheckEmptyUtil.isNotEmpty(e.getRoleName()))
            .map(LoginServiceDto::getRoleName)
            .distinct()
            .collect(Collectors.toList());
    // 生成token
    String token =
        JwtTokenUtil.createToken(
            entity.getUserId(),
            entity.getUserName(),
            entity.getNickName(),
            entity.getShopId(),
            entity.getShopName(),
            roleNameList,
            permissionCodeList,
            permissionNameList);
    log.info("create token success：{}", token);
    LoginResultDto result = new LoginResultDto();
    result.setToken(JwtKeyEnum.TOKEN_PREFIX.getKey() + token);
    return result;
  }

  /** 用户注册. */
  @Override
  public Boolean register(RegistryParamsDto paramsDto) {
    // 1：判断该手机号是否存在
    Users entity = userRepository.findByPhone(paramsDto.getPhone());
    if (CheckEmptyUtil.isNotEmpty(entity)) {
      throw new LoginException("该手机号已经注册");
    }
    // 2：设置用户信息
    entity = new Users();
    // 将密码加密
    String password = encoder.encode(paramsDto.getPassword());
    entity.setPassword(password);
    entity.setSex("0");
    entity.setAccountBalance(new BigDecimal("0.00"));
    entity.setPhone(paramsDto.getPhone());
    entity.setSourceType("1");
    entity.setStatus("Y");
    entity.setIsEmailCheck("0");
    entity.setIsMobileCheck("0");
    entity.setUserLevel(1); // 会员等级
    entity.setPoints(0); // 会员积分
    entity.setExperienceValue(0); // 经验
    entity.setCreateTime(new Date());
    entity.setUpdateTime(new Date());
    // 保存到数据库
    userRepository.save(entity);
    return true;
  }

  /** 发送短信验证码. */
  @Override
  public Boolean sendMessageCode(RegistryParamsDto paramsDto) {
    // 1：生成6位数验证码
    String phoneCode = RandomStringUtils.randomNumeric(6);
    // 2：将手机号作为key,验证码作为value,存到redis中
    stringRedisTemplate
        .opsForValue()
        .set(REDIS_PHONE_CODE_PREFIX + paramsDto.getPhone(), phoneCode, 2L, TimeUnit.MINUTES);
    // 3：创建mq入参对象.
    SendPhoneCodeParamsDto param = new SendPhoneCodeParamsDto();
    param.setPhoneNumbers(paramsDto.getPhone());
    Map<String, String> templateParam = new HashMap<>();
    templateParam.put("code", phoneCode);
    param.setTemplateParam(templateParam);

    log.info("发送短信mq发送者：{}", paramsDto.toString());
    amqpTemplate.convertAndSend(EXCHANGE_NAME, "phoneCode", param);
    return true;
  }
}
